﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;

using Jarvis.Utility;

namespace Jarvis.Learners
{
    class Learner
    {
        public String _name = null;
        public Dictionary<String, uint> classCount;
        public String lastLabel;
        public List<Example> examples;

        public Learner()
        {
            lastLabel = "";
            classCount = new Dictionary<String, uint>();
        }

        public void RemoveLastExample()
        {
            if (examples.Count > 0)
            {
                examples.RemoveAt(examples.Count - 1);
            }

            this.Train();

            this.Save();
        }

        public virtual void Load()
        {
            String appDataFolder = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\The Pennyworth Project\\Jarvis\\Learners\\";
            String appData = appDataFolder + name + ".learner";

            if (File.Exists(appData))
            {
                IFormatter formatter = new BinaryFormatter();

                Stream s = new FileStream(appData, FileMode.Open, FileAccess.Read, FileShare.Read);
                examples = (List<Example>)formatter.Deserialize(s);
                s.Close();
            }
            else
                examples = new List<Example>();

            List<String> labels = new List<string>();

            foreach (Example e in examples)
            {
                if (!labels.Contains(e.label))
                    labels.Add(e.label);
            }

            Preferences.preferences.Set(this.name + " Labels", labels);

            this.Train();
        }

        public static List<Example> CopyExamplesList(List<Example> list)
        {
            List<Example> new_list = new List<Example>();

            foreach (Example e in list)
            {
                Dictionary<string, object> new_state = new Dictionary<string,object>();

                foreach (string key in e.state.Keys)
                {
                    object o = e.state[key];

                    if (o is List<object>)
                    {
                        List<object> new_o = new List<object>();

                        foreach (var obj in (o as List<object>))
                            new_o.Add(obj);

                        o = new_o;
                    }

                    new_state[key] = o;
                }

                Example new_e = new Example(e.label, new_state);

                new_list.Add(new_e);
            }

            return new_list;

            /* IFormatter formatter = new BinaryFormatter();

            Stream s = new MemoryStream();
            formatter.Serialize(s, list);

            s.Seek(0, SeekOrigin.Begin);

            List<Example> new_examples = (List<Example>)formatter.Deserialize(s);

            return new_examples; */
        }

        public void Save()
        {
            if (name != null)
            {
                String appDataFolder = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\The Pennyworth Project\\Jarvis\\Learners\\";
                String appData = appDataFolder + name + ".learner";

                if (!Directory.Exists(appDataFolder))
                {
                    Directory.CreateDirectory(appDataFolder);

                    this.Save();
                }
                else
                {
                    if (examples != null)
                    {
                        IFormatter formatter = new BinaryFormatter();

                        Stream s = new FileStream(appData, FileMode.Create, FileAccess.Write, FileShare.Read);
                        formatter.Serialize(s, examples);

                        s.Close();
                    }
                }

                List<String> labels = new List<string>();

                foreach (Example e in examples)
                {
                    if (!labels.Contains(e.label))
                        labels.Add(e.label);
                }

                Preferences.preferences.Set(this.name + " Labels", labels);
            }
        }

        public String name
        {
            get
            {
                return _name;
            }

            set
            {
                _name = value;
    
                this.Load();
            }
        }
        
        public void AddExampleForClass(Dictionary<string, object> state, String label)
        {
            Example example = new Example(label, state);

            if (examples == null)
                examples = new List<Example>();

            examples.Add(example);

            uint count = 0;

            if (classCount.ContainsKey(example.label))
                count = classCount[example.label];

            classCount[example.label] = count + 1;

            this.Save();
            this.Train();

            List<String> labels = (Preferences.preferences.Get(this.name + " Labels") as List<String>);

            if (!labels.Contains(label))
                labels.Add(label);
        }

        public virtual void Train()
        {
            Console.WriteLine("learner train");
        }

        public virtual String GetPrediction(Dictionary<String, object> state)
        {
            uint max = 0;
            String label = null;

            foreach (String key in classCount.Keys)
            {
                if (classCount[key] > max)
                {
                    max = classCount[key];
                    label = key;
                }
            }

            return label;
        }

        public uint ExampleCount()
        {
            return (uint) examples.Count;
        }
        
        public void Reset()
        {
            examples = new List<Example>();

            lastLabel = "";

            classCount = new Dictionary<String, uint>();
        }
    }
}
